//
//  GM_MKMapView.m
//  GMMapSDKTest
//
//  Created by zhuch on 16/8/11.
//  Copyright © 2016年 goome. All rights reserved.
//

#import "GM_MKMapView.h"
#import "GM_MKAnnotationView.h"
#import "GM_MKCircleView.h"
#import "GM_MKPolylineRenderer.h"
#import "GM_MKPolygonRenderer.h"

#define MERCATOR_OFFSET 268435456
#define MERCATOR_RADIUS 85445659.44705395

@interface GM_MKMapView () <MKMapViewDelegate>

@end

@implementation GM_MKMapView
@synthesize zoomLevel = _zoomLevel;

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.delegate = self;
    }
    return self;
}

- (NSInteger)zoomLevel {
    return _zoomLevel;
}
- (void)setZoomLevel:(NSInteger)zoomLevel {
    _zoomLevel = zoomLevel;
    [self setCenterCoordinate:self.centerCoordinate zoomLevel:self.zoomLevel animated:YES];
}

#pragma mark -
#pragma mark Map conversion methods

- (double)longitudeToPixelSpaceX:(double)longitude
{
    return round(MERCATOR_OFFSET + MERCATOR_RADIUS * longitude * M_PI / 180.0);
}

- (double)latitudeToPixelSpaceY:(double)latitude
{
    return round(MERCATOR_OFFSET - MERCATOR_RADIUS * logf((1 + sinf(latitude * M_PI / 180.0)) / (1 - sinf(latitude * M_PI / 180.0))) / 2.0);
}

- (double)pixelSpaceXToLongitude:(double)pixelX
{
    return ((round(pixelX) - MERCATOR_OFFSET) / MERCATOR_RADIUS) * 180.0 / M_PI;
}

- (double)pixelSpaceYToLatitude:(double)pixelY
{
    return (M_PI / 2.0 - 2.0 * atan(exp((round(pixelY) - MERCATOR_OFFSET) / MERCATOR_RADIUS))) * 180.0 / M_PI;
}

#pragma mark -
#pragma mark Helper methods

- (MKCoordinateSpan)coordinateSpanWithMapView:(MKMapView *)mapView
                             centerCoordinate:(CLLocationCoordinate2D)centerCoordinate
                                 andZoomLevel:(NSUInteger)zoomLevel
{
    // convert center coordiate to pixel space
    double centerPixelX = [self longitudeToPixelSpaceX:centerCoordinate.longitude];
    double centerPixelY = [self latitudeToPixelSpaceY:centerCoordinate.latitude];
    
    // determine the scale value from the zoom level
    NSInteger zoomExponent = 20 - zoomLevel;
    double zoomScale = pow(2, zoomExponent);
    
    // scale the map’s size in pixel space
    CGSize mapSizeInPixels = mapView.bounds.size;
    double scaledMapWidth = mapSizeInPixels.width * zoomScale;
    double scaledMapHeight = mapSizeInPixels.height * zoomScale;
    
    // figure out the position of the top-left pixel
    double topLeftPixelX = centerPixelX - (scaledMapWidth / 2);
    double topLeftPixelY = centerPixelY - (scaledMapHeight / 2);
    
    // find delta between left and right longitudes
    CLLocationDegrees minLng = [self pixelSpaceXToLongitude:topLeftPixelX];
    CLLocationDegrees maxLng = [self pixelSpaceXToLongitude:topLeftPixelX + scaledMapWidth];
    CLLocationDegrees longitudeDelta = maxLng - minLng;
    
    // find delta between top and bottom latitudes
    CLLocationDegrees minLat = [self pixelSpaceYToLatitude:topLeftPixelY];
    CLLocationDegrees maxLat = [self pixelSpaceYToLatitude:topLeftPixelY + scaledMapHeight];
    CLLocationDegrees latitudeDelta = -1 * (maxLat - minLat);
    
    // create and return the lat/lng span
    MKCoordinateSpan span = MKCoordinateSpanMake(latitudeDelta, longitudeDelta);
    return span;
}

#pragma mark -
#pragma mark Public methods

- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
                  zoomLevel:(NSUInteger)zoomLevel
                   animated:(BOOL)animated
{
    // clamp large numbers to 28
    CGFloat level = MIN(zoomLevel, 28);
    
    // use the zoom level to compute the region
    MKCoordinateSpan span = [self coordinateSpanWithMapView:self centerCoordinate:centerCoordinate andZoomLevel:level];
    MKCoordinateRegion region = MKCoordinateRegionMake(centerCoordinate, span);
    
    // set the region like normal
    [self setRegion:region animated:animated];
}

#pragma mark - GMMapViewDelegate
- (void)gm_ZoomIn
{
    NSInteger level = self.zoomLevel;
    level ++;
    if (level >= 19) {
        level = 19;
    }
    NSLog(@"zoIn -> %@", @(level));
    self.zoomLevel = level;
}

- (void)gm_ZoomOut
{
    NSInteger level = self.zoomLevel;
    level --;
    if (level <= 1) {
        level = 1;
    }
    NSLog(@"zoOut -> %@", @(level));
    self.zoomLevel = level;
}

- (void)gm_setMapType:(NSInteger)mapType
{
    if (mapType == 0) {
        [self setMapType:MKMapTypeStandard];
    }
    else if (mapType == 1)
    {
        [self setMapType:MKMapTypeSatellite];
    }
}

- (void)gm_SetCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated
{
    [self setCenterCoordinate:coordinate zoomLevel:self.zoomLevel animated:animated];
}

- (void)gm_AddAnnotation:(id<GMAnnotation>)annotation
{
    [self addAnnotation:(id<MKAnnotation>)annotation];
}

- (void)gm_AddAnnotations:(NSArray *)annotations
{
    [self addAnnotations:annotations];
}

- (void)gm_RemoveAnnotation:(id<GMAnnotation>)annotation
{
    [self removeAnnotation:(id<MKAnnotation>)annotation];
}

- (void)gm_RemoveAnnotations:(NSArray *)annotations
{
    [self removeAnnotations:annotations];
}

- (void)gm_SelectAnnotation:(id<GMAnnotation>)annotation animated:(BOOL)animated
{
    [self selectAnnotation:(id<MKAnnotation>)annotation animated:animated];
}

- (void)gm_DeselectAnnotation:(id<GMAnnotation>)annotation animated:(BOOL)animated
{
    [self deselectAnnotation:(id<MKAnnotation>)annotation animated:animated];
}

- (void)gm_ShowAnnotations:(NSArray *)annotations animated:(BOOL)animated
{
    [self showAnnotations:annotations animated:animated];
}

- (void)gm_AddOverlay:(id<GMOverlay>)overlay
{
    [self addOverlay:(id<MKOverlay>)overlay];
}

- (void)gm_AddOverlays:(NSArray *)overlays;
{
    [self addOverlays:overlays];
}

- (void)gm_RemoveOverlay:(id<GMOverlay>)overlay;
{
    [self removeOverlay:(id<MKOverlay>)overlay];
}

- (void)gm_RemoveOverlays:(NSArray *)overlays;
{
    [self removeOverlays:overlays];
}

- (void)gm_InsertOverlay:(id<GMOverlay>)overlay atIndex:(NSUInteger)index;
{
    [self insertOverlay:(id<MKOverlay>)overlay atIndex:index];
}

- (void)gm_ExchangeOverlayAtIndex:(NSUInteger)index1 withOverlayAtIndex:(NSUInteger)index2;
{
    [self exchangeOverlayAtIndex:index1 withOverlayAtIndex:index2];
}

- (void)gm_InsertOverlay:(id<GMOverlay>)overlay aboveOverlay:(id<GMOverlay>)sibling;
{
    [self insertOverlay:(id<MKOverlay>)overlay aboveOverlay:(id<MKOverlay>)sibling];
}

- (void)gm_InsertOverlay:(id<GMOverlay>)overlay belowOverlay:(id<GMOverlay>)sibling;
{
    [self insertOverlay:(id<MKOverlay>)overlay belowOverlay:(id<MKOverlay>)sibling];
}

- (id<GMAnnotationViewDelegate>)gm_dequeueReusableAnnotationViewWithIdentifier:(NSString *)identifier
{
    return (id<GMAnnotationViewDelegate>)[self dequeueReusableAnnotationViewWithIdentifier:identifier];
}

- (CLLocationCoordinate2D)gm_ConvertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view
{
    return [self convertPoint:point toCoordinateFromView:view];
}

#pragma mark - MKMapViewDelegate

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_mapView:viewForAnnotation:)]) {
        GM_MKAnnotationView * annotationView = (GM_MKAnnotationView *)[self.gm_delegate gm_mapView:self viewForAnnotation:(id<GMAnnotation>)annotation];
        return annotationView;
    }
    return nil;
}

- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_mapView:didAddAnnotationViews:)]) {
        [self.gm_delegate gm_mapView:self didAddAnnotationViews:views];
    }
}

- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_mapView:didDeselectAnnotationView:)]) {
        [self.gm_delegate gm_mapView:self didDeselectAnnotationView:(id<GMAnnotationViewDelegate>)view];
    }
}

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_mapView:didSelectAnnotationView:)]) {
        [self.gm_delegate gm_mapView:self didSelectAnnotationView:(id<GMAnnotationViewDelegate>)view];
    }
}

- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_mapView:viewForOverlay:)]) {
        if ([overlay isKindOfClass:[MKCircle class]]) {
            GM_MKCircleView * gm_mkCircleView = (GM_MKCircleView *)[self.gm_delegate gm_mapView:self viewForOverlay:(id<GMOverlay>)overlay];
            return gm_mkCircleView;
        }
        else if ([overlay isKindOfClass:[MKPolyline class]])
        {
            GM_MKPolylineRenderer * gm_mkPolylineView = (GM_MKPolylineRenderer *)[self.gm_delegate gm_mapView:self viewForOverlay:(id<GMOverlay>)overlay];
            return gm_mkPolylineView;
        }
        else if ([overlay isKindOfClass:[MKPolygon class]])
        {
            GM_MKPolygonRenderer * gm_mkPolygonView = (GM_MKPolygonRenderer *)[self.gm_delegate gm_mapView:self viewForOverlay:(id<GMOverlay>)overlay];
            return gm_mkPolygonView;
        }
    }
    return nil;
}

- (void)mapView:(MKMapView *)mapView annotationViewForBubble:(MKAnnotationView *)view
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_mapView:annotationViewForBubble:)]) {
        [self.gm_delegate gm_mapView:self annotationViewForBubble:(id<GMAnnotationViewDelegate>)view];
    }
}

- (void)mapView:(MKMapView *)mapView didAddOverlayViews:(NSArray *)overlayViews
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_mapView:didAddOverlayViews:)]) {
        [self.gm_delegate gm_mapView:self didAddOverlayViews:overlayViews];
    }
}

- (void)mapView:(id<GMMapViewDelegate>)mapView regionWillChangeAnimated:(BOOL)animated
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_MapView:regionWillChangeAnimated:)]) {
        [self.gm_delegate gm_MapView:self regionWillChangeAnimated:animated];
    }
}


- (void)mapView:(id<GMMapViewDelegate>)mapView regionDidChangeAnimated:(BOOL)animated
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_MapView:regionDidChangeAnimated:)]) {
        [self.gm_delegate gm_MapView:self regionDidChangeAnimated:animated];
    }
}


- (void)mapView:(id<GMMapViewDelegate>)mapView mapWillMoveByUser:(BOOL)wasUserAction;
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_MapView:mapWillZoomByUser:)]) {
        [self.gm_delegate gm_MapView:self mapWillMoveByUser:wasUserAction];
    }
}


- (void)mapView:(id<GMMapViewDelegate>)mapView mapDidMoveByUser:(BOOL)wasUserAction;
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_MapView:mapDidMoveByUser:)]) {
        [self.gm_delegate gm_MapView:self mapDidMoveByUser:wasUserAction];
    }
}


- (void)mapView:(id<GMMapViewDelegate>)mapView mapWillZoomByUser:(BOOL)wasUserAction
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_MapView:mapWillZoomByUser:)]) {
        [self.gm_delegate gm_MapView:self mapWillZoomByUser:wasUserAction];
    }
}


- (void)mapView:(id<GMMapViewDelegate>)mapView mapDidZoomByUser:(BOOL)wasUserAction
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_MapView:mapDidZoomByUser:)]) {
        [self.gm_delegate gm_MapView:self mapDidZoomByUser:wasUserAction];
    }
}


- (void)mapView:(id<GMMapViewDelegate>)mapView didSingleTappedAtCoordinate:(CLLocationCoordinate2D)coordinate
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_MapView:didSingleTappedAtCoordinate:)]) {
        [self.gm_delegate gm_MapView:self didSingleTappedAtCoordinate:coordinate];
    }
}

- (void)mapView:(id<GMMapViewDelegate>)mapView didLongPressedAtCoordinate:(CLLocationCoordinate2D)coordinate
{
    if (self.gm_delegate && [self.gm_delegate respondsToSelector:@selector(gm_MapView:didLongPressedAtCoordinate:)]) {
        [self.gm_delegate gm_MapView:self didLongPressedAtCoordinate:coordinate];
    }
}

@end
